/*
 * File      : interrupt.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006, RT-Thread Development Team
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://openlab.rt-thread.com/license/LICENSE
 *
 * Change Logs:
 * Date                Author       Notes
 * 2011-03-26     lgnq           first version
 */

#include <rtthread.h>
#include "dm368.h"

#define MAX_HANDLERS	64

#define IRQ_BIT(irq)		((irq) & 0x1f)

extern rt_uint32_t rt_interrupt_nest;

/* exception and interrupt handler table */
rt_isr_handler_t isr_table[MAX_HANDLERS];
rt_uint32_t rt_interrupt_from_thread, rt_interrupt_to_thread;
rt_uint32_t rt_thread_switch_interrput_flag;
static rt_uint8_t dm646x_default_priorities[] = {
	[IRQ_DM646X_VP_VERTINT0]        = 7,
	[IRQ_DM646X_VP_VERTINT1]        = 7,
	[IRQ_DM646X_VP_VERTINT2]        = 7,
	[IRQ_DM646X_VP_VERTINT3]        = 7,
	[IRQ_DM646X_VP_ERRINT]          = 7,
	[IRQ_DM646X_RESERVED_1]         = 7,
	[IRQ_DM646X_RESERVED_2]         = 7,
	[IRQ_DM646X_WDINT]              = 7,
	[IRQ_DM646X_CRGENINT0]          = 7,
	[IRQ_DM646X_CRGENINT1]          = 7,
	[IRQ_DM646X_TSIFINT0]           = 7,
	[IRQ_DM646X_TSIFINT1]           = 7,
	[IRQ_DM646X_VDCEINT]            = 7,
	[IRQ_DM646X_USBINT]             = 7,
	[IRQ_DM646X_USBDMAINT]          = 7,
	[IRQ_DM646X_PCIINT]             = 7,
	[IRQ_CCINT0]                    = 7,    /* dma */
	[IRQ_CCERRINT]                  = 7,    /* dma */
	[IRQ_TCERRINT0]                 = 7,    /* dma */
	[IRQ_TCERRINT]                  = 7,    /* dma */
	[IRQ_DM646X_TCERRINT2]          = 7,
	[IRQ_DM646X_TCERRINT3]          = 7,
	[IRQ_DM646X_IDE]                = 7,
	[IRQ_DM646X_HPIINT]             = 7,
	[IRQ_DM646X_EMACRXTHINT]        = 7,
	[IRQ_DM646X_EMACRXINT]          = 7,
	[IRQ_DM646X_EMACTXINT]          = 7,
	[IRQ_DM646X_EMACMISCINT]        = 7,
	[IRQ_DM646X_MCASP0TXINT]        = 7,
	[IRQ_DM646X_MCASP0RXINT]        = 7,
	[IRQ_AEMIFINT]                  = 7,
	[IRQ_DM646X_RESERVED_3]         = 7,
	[IRQ_DM646X_MCASP1TXINT]        = 7,    /* clockevent */
	[IRQ_TINT0_TINT34]              = 7,    /* clocksource */
	[IRQ_TINT1_TINT12]              = 7,    /* DSP timer */
	[IRQ_TINT1_TINT34]              = 7,    /* system tick */
	[IRQ_PWMINT0]                   = 7,
	[IRQ_PWMINT1]                   = 7,
	[IRQ_DM646X_VLQINT]             = 7,
	[IRQ_I2C]                       = 7,
	[IRQ_UARTINT0]                  = 7,
	[IRQ_UARTINT1]                  = 7,
	[IRQ_DM646X_UARTINT2]           = 7,
	[IRQ_DM646X_SPINT0]             = 7,
	[IRQ_DM646X_SPINT1]             = 7,
	[IRQ_DM646X_DSP2ARMINT]         = 7,
	[IRQ_DM646X_RESERVED_4]         = 7,
	[IRQ_DM646X_PSCINT]             = 7,
	[IRQ_DM646X_GPIO0]              = 7,
	[IRQ_DM646X_GPIO1]              = 7,
	[IRQ_DM646X_GPIO2]              = 7,
	[IRQ_DM646X_GPIO3]              = 7,
	[IRQ_DM646X_GPIO4]              = 7,
	[IRQ_DM646X_GPIO5]              = 7,
	[IRQ_DM646X_GPIO6]              = 7,
	[IRQ_DM646X_GPIO7]              = 7,
	[IRQ_DM646X_GPIOBNK0]           = 7,
	[IRQ_DM646X_GPIOBNK1]           = 7,
	[IRQ_DM646X_GPIOBNK2]           = 7,
	[IRQ_DM646X_DDRINT]             = 7,
	[IRQ_DM646X_AEMIFINT]           = 7,
	[IRQ_COMMTX]                    = 7,
	[IRQ_COMMRX]                    = 7,
	[IRQ_EMUINT]                    = 7,
};

rt_isr_handler_t rt_hw_interrupt_handle(rt_uint32_t vector)
{
	rt_kprintf("Unhandled interrupt 0x%x occured!!!\n", vector);
	rt_kprintf("FIQ0 = 0x%x\n", readl(AINTC_FIQ0));
	rt_kprintf("FIQ1 = 0x%x\n", readl(AINTC_FIQ1));	
	rt_kprintf("IRQ0 = 0x%x\n", readl(AINTC_IRQ0));
	rt_kprintf("IRQ1 = 0x%x\n", readl(AINTC_IRQ1));
	rt_kprintf("EINT0 = 0x%x\n", readl(AINTC_EINT0));
	rt_kprintf("EINT1 = 0x%x\n", readl(AINTC_EINT1));
	return RT_NULL;
}

void rt_hw_interrupt_init(void)
{
	register rt_uint32_t idx;
	rt_uint32_t i;
	const rt_uint8_t *davinci_def_priorities = dm646x_default_priorities;

	/* Clear all interrupt requests */
	writel(~0x0, AINTC_FIQ0); 
	writel(~0x0, AINTC_FIQ1); 
	writel(~0x0, AINTC_IRQ0); 
	writel(~0x0, AINTC_IRQ1); 

	/* Disable all interrupts */
	writel(0x0, AINTC_EINT0); 
	writel(0x0, AINTC_EINT1); 

	/* Interrupts disabled immediately, IRQ entry reflects all */
	writel(0x0, AINTC_INTCTL);

	/* we don't use the hardware vector table, just its entry addresses */
	writel(isr_table, AINTC_EABASE);
	
	/* Clear all interrupt requests */
	writel(~0x0, AINTC_FIQ0);
	writel(~0x0, AINTC_FIQ1);
	writel(~0x0, AINTC_IRQ0);
	writel(~0x0, AINTC_IRQ1);

	for (i = AINTC_INTPRI0; i <= AINTC_INTPRI7; i += 4) 
	{
		unsigned	j;
		rt_uint32_t		pri;

		for (j = 0, pri = 0; j < 32; j += 4, davinci_def_priorities++)
			pri |= (*davinci_def_priorities & 0x07) << j;
		writel(pri, i);
	}

	/* init exceptions table */
	for (idx=0; idx < MAX_HANDLERS; idx++)
	{
		isr_table[idx] = (rt_isr_handler_t)rt_hw_interrupt_handle;
	}

	/* init interrupt nest, and context in thread sp */
	rt_interrupt_nest = 0;
	rt_interrupt_from_thread = 0;
	rt_interrupt_to_thread = 0;
	rt_thread_switch_interrput_flag = 0;
}

static void davinci_mask_irq(unsigned int irq)
{
	unsigned int mask;
	rt_uint32_t l;

	mask = 1 << IRQ_BIT(irq);

	if (irq > 31) 
	{
		l = readl(AINTC_EINT1);
		l &= ~mask;
		writel(l, AINTC_EINT1);
	} 
	else 
	{
		l = readl(AINTC_EINT0);
		l &= ~mask;
		writel(l, AINTC_EINT0);
	}
}

static void davinci_unmask_irq(unsigned int irq)
{
	unsigned int mask;
	rt_uint32_t l;

	mask = 1 << IRQ_BIT(irq);

	if (irq > 31) 
	{
		l = readl(AINTC_EINT1);
		l |= mask;
		writel(l, AINTC_EINT1);
	} 
	else 
	{
		l = readl(AINTC_EINT0);
		l |= mask;
		writel(l, AINTC_EINT0);
	}
}

static void davinci_ack_irq(unsigned int irq)
{
	unsigned int mask;

	mask = 1 << IRQ_BIT(irq);

	if (irq > 31)
		writel(mask, AINTC_IRQ1);
	else
		writel(mask, AINTC_IRQ0);
}


/**
 * This function will mask a interrupt.
 * @param vector the interrupt number
 */
void rt_hw_interrupt_mask(int irq)
{
	davinci_mask_irq(irq);
}

/**
 * This function will un-mask a interrupt.
 * @param vector the interrupt number
 */
void rt_hw_interrupt_umask(int irq)
{
	davinci_unmask_irq(irq);
}

void rt_hw_interrupt_ack(int irq)
{
	davinci_ack_irq(irq);
}

/**
 * This function will install a interrupt service routine to a interrupt.
 * @param vector the interrupt number
 * @param new_handler the interrupt service routine to be installed
 * @param old_handler the old interrupt service routine
 */
void rt_hw_interrupt_install(int vector, rt_isr_handler_t new_handler, rt_isr_handler_t *old_handler)
{
	if (vector < MAX_HANDLERS)
	{
		if (*old_handler != RT_NULL) 
			*old_handler = isr_table[vector];
		if (new_handler != RT_NULL) 
			isr_table[vector] = new_handler;
	}
}

/*@}*/

